package com.zhuiyun.project.config;

import com.zhuiyun.project.security.commonhandler.UserLoginAuthenticationFailureHandler;
import com.zhuiyun.project.security.commonhandler.UserLoginAuthenticationSuccessHandler;
import com.zhuiyun.project.security.customizelogin.email.EmailCodeAuthenticationFilter;
import com.zhuiyun.project.security.customizelogin.email.EmailCodeAuthenticationProvider;
import com.zhuiyun.project.security.customizelogin.phone.PhoneCodeAuthenticationFilter;
import com.zhuiyun.project.security.customizelogin.phone.PhoneCodeAuthenticationProvider;
import com.zhuiyun.project.security.customizelogin.usernamepassword.UsernamePasswordAuthenticationProvider;
import com.zhuiyun.project.security.customizelogin.usernamepassword.UsernamePasswordFilter;
import com.zhuiyun.project.security.customizelogin.usernamepasswordcode.UsernamePasswordCodeAuthenticationProvider;
import com.zhuiyun.project.security.customizelogin.usernamepasswordcode.UsernamePasswordCodeFilter;
import com.zhuiyun.project.security.jwt.ExceptionHandlerFilter;
import com.zhuiyun.project.security.jwt.JWTAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.util.Arrays;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * @ClassName WebSecurityConfig
 * @Description Security 权限配置
 * @Author zcy
 * @Date 2023/3/27 10:28
 **/
@Configuration
public class WebSecurityConfig  implements WebMvcConfigurer {


    /**
     * 自定登录失败做出的返回结果
     */
    @Autowired
    AuthenticationEntryPoint authExceptionEntryPoint;
    /**
     * 自定义登出处理逻辑
     */
    @Autowired
    LogoutSuccessHandler logoutSuccessHandler;
    /**
     *自定义用户名密码验证码信息认证
     */
    @Resource
    UsernamePasswordCodeAuthenticationProvider usernamePasswordCodeAuthenticationProvider;
    /**
     * 自定义邮箱认证
     */
    @Resource
    EmailCodeAuthenticationProvider emailCodeAuthenticationProvider;
    /**
     * 自定义手机号认证
     */
    @Resource
    PhoneCodeAuthenticationProvider phoneCodeAuthenticationProvider;
    /**
     * 自定义 用户名密码认证
     */
    @Resource
    UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;
    /**
     * 登录失败处理逻辑
     */
    @Autowired
    UserLoginAuthenticationFailureHandler userLoginAuthenticationFailureHandler;

    /**
     * 自定义用户登录成功操作
     */
    @Resource
    UserLoginAuthenticationSuccessHandler userLoginAuthenticationSuccessHandler;

    /**
     * 捕获token异常
     */
    @Autowired
    ExceptionHandlerFilter exceptionHandlerFilter;

    /**
     * 声明密码加密方式
     * @return
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * @Author zcy
     * @Description 允许跨域
     * @Date 10:29 2023/3/10
     * @param registry
     * @Return void
     **/
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 允许跨域访问路径
        registry.addMapping("/**")
                // 允许跨域访问的源
                .allowedOrigins("*")
                // 允许请求方式
                .allowedMethods("POST","GET","PUT","DELETE","OPTIONS")
                // 预检间隔时间
                .maxAge(168000)
                // 允许头部设置
                .allowedHeaders("*");
    }
    /**
     * 放行路径
     */
    private  static  final  String[]  AUTH_WHITELIST  =  {
            // -- swagger ui
            "/swagger-resources/**",
            "/swagger-ui.html",
            "/swagger-ui/",
            "/v3/api-docs",
            "/v2/api-docs",
            "/swagger-ui/**",
            "/webjars/**",
            "/images/**",
            "/js/**",
            "/webjars/**",
            "/druid/**",
            "/sysUser/code",
            "/captchaImage",
            "/sysUser/getListByLoginName",
            "/sysUser/getUserByToken",
    };

    /**
     * 注册 Security相关自定义过滤器
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests((auth) -> {
                            try {
                                auth.antMatchers(AUTH_WHITELIST).permitAll()
                                        .anyRequest().authenticated()
                                        //异常处理(权限拒绝、登录失效等)
                                        .and().exceptionHandling().
                                            authenticationEntryPoint(authExceptionEntryPoint)//匿名用户访问无权限资源时的异常处理
                                        // 登出
                                        .and().logout().
                                            permitAll().
                                            logoutSuccessHandler(logoutSuccessHandler) //登出成功处理逻辑
                                        .and().sessionManagement().
                                            sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                                        .and().cors().configurationSource(configurationSource()); // 允许跨域;//禁用session
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }
                )
                .httpBasic(withDefaults());
        // 禁用缓存
        http.headers().cacheControl();
        // 添加JWT过滤器
        http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager(http)));
        // 注册异常捕获过滤器
        http.addFilterBefore(exceptionHandlerFilter, CorsFilter.class);
       // 注册密码登录
        http.addFilterAt(usernamePasswordFilter(http), UsernamePasswordAuthenticationFilter.class);
        // 注册用户名密码验证码登录
        http.addFilterAt(usernamePasswordCodeFilter(http), UsernamePasswordAuthenticationFilter.class);
        // 注册邮箱验证码登录
        http.addFilterAt(emailLoginFilter(http), UsernamePasswordAuthenticationFilter.class);
        // 注册手机号验证码登录
        http.addFilterAt(phoneCodeAuthenticationFilter(http), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }


    /**
     * 注册自定义登录
     * @param httpSecurity
     * @return
     * @throws Exception
     */
    @Bean("authenticationManager")
    @Primary
    AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .build();
        return authenticationManager;
    }
    @Bean("authenticationManagerCode")
    AuthenticationManager authenticationManagerCode(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(usernamePasswordCodeAuthenticationProvider)
                .build();
        return authenticationManager;
    }
    @Bean("authenticationManagerPassword")
    AuthenticationManager authenticationManagerPassword(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(usernamePasswordAuthenticationProvider)
                .build();
        return authenticationManager;
    }
    @Bean("authenticationManagerEmail")
    AuthenticationManager authenticationManagerEmail(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(emailCodeAuthenticationProvider)
                .build();
        return authenticationManager;
    }
    @Bean("authenticationManagerPhone")
    AuthenticationManager authenticationManagerPhone(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(phoneCodeAuthenticationProvider)
                .build();
        return authenticationManager;
    }
    /**
     * 注册自定义用户 密码 验证码 登录拦截器
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public UsernamePasswordCodeFilter usernamePasswordCodeFilter(HttpSecurity http) throws Exception {
        UsernamePasswordCodeFilter usernamePasswordCodeFilter = new UsernamePasswordCodeFilter("/userCode/login");
        usernamePasswordCodeFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
        usernamePasswordCodeFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
        usernamePasswordCodeFilter.setAuthenticationManager(authenticationManagerCode(http));
        return usernamePasswordCodeFilter;
    }

    /**
     * 注册自定义 用户 密码 登录拦截器
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public UsernamePasswordFilter usernamePasswordFilter(HttpSecurity http) throws Exception {
        UsernamePasswordFilter usernamePasswordFilter = new UsernamePasswordFilter("/user/login");
        usernamePasswordFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
        usernamePasswordFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
        usernamePasswordFilter.setAuthenticationManager(authenticationManagerPassword(http));
        return usernamePasswordFilter;
    }
    /**
     * 注册邮箱 验证码 登录拦截器
     * @return
     * @return
     * @throws Exception
     */
    @Bean
    public EmailCodeAuthenticationFilter emailLoginFilter(HttpSecurity http) throws Exception {
        EmailCodeAuthenticationFilter emailLoginFilter = new EmailCodeAuthenticationFilter("/email/login");
        emailLoginFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
        emailLoginFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
        emailLoginFilter.setAuthenticationManager(authenticationManagerEmail(http));
        return emailLoginFilter;
    }
    /**
     * 注册手机号 验证码 登录拦截器
     * @return
     * @return
     * @throws Exception
     */
    @Bean
    public PhoneCodeAuthenticationFilter phoneCodeAuthenticationFilter(HttpSecurity http) throws Exception {
        PhoneCodeAuthenticationFilter phoneCodeAuthenticationFilter = new PhoneCodeAuthenticationFilter("/phone/login");
        phoneCodeAuthenticationFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
        phoneCodeAuthenticationFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
        phoneCodeAuthenticationFilter.setAuthenticationManager(authenticationManagerPhone(http));
        return phoneCodeAuthenticationFilter;
    }

    /**
     * @Description 允许跨域
     * @Author lzy
     * @Date 2023/4/8 11:31
     * @Return org.springframework.web.cors.CorsConfigurationSource

     */
    CorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }

}
